/* QtLua -- Lua bindings for Qt
   Copyright (C) 2011, Jarek Pelczar

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 3 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General
   Public License along with this library; if not, write to the
   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301 USA

*/

#include "qluascriptenginedebugger_p.h"
#include "qluascriptdebugger_p.h"
#include "qluadebuggeragent_p.h"
#include "qluascriptdebuggerdefaultwidgetfactory_p.h"
#include <QMenu>
#include <QToolBar>
#include <QSettings>
#include <QApplication>
#include <QVBoxLayout>
#include <QEvent>
#include <QDockWidget>
#include <QMenuBar>
#include <QDebug>

class DebuggerWindowCloseNotifier : public QObject
{
    Q_OBJECT
public:
    DebuggerWindowCloseNotifier(QWidget * w, QObject * parent = 0)
        : QObject(parent),
          widget(w)
    {
        w->installEventFilter(this);
    }

    bool eventFilter(QObject * o, QEvent * e)
    {
        if(o != widget)
            return false;
        if(e->type() != QEvent::Close)
            return false;
#ifdef QT_DEBUG
        qDebug() << "Debugger window closed";
#endif
        emit widgetClosed();
        return true;
    }

signals:
    void widgetClosed();

private:
    QWidget * widget;
};

QLuaScriptEngineDebuggerPrivate::QLuaScriptEngineDebuggerPrivate()
    : standardWindow(NULL),
      autoShow(true),
      debugger(NULL)
{
}

QLuaScriptEngineDebuggerPrivate::~QLuaScriptEngineDebuggerPrivate()
{
    delete debugger;

    if(standardWindow) {
        QSettings settings;
        QByteArray geometry = standardWindow->saveGeometry();
        settings.setValue("QtLua/debugger/mainWindowGeometry", geometry);
        QByteArray state = standardWindow->saveState();
        settings.setValue("QtLua/debugger/mainWindowState", state);
        if(!standardWindow->parent())
            delete standardWindow;
    }
}

void QLuaScriptEngineDebuggerPrivate::_q_showStandardWindow()
{
    q->standardWindow();
    standardWindow->show();
}

void QLuaScriptEngineDebuggerPrivate::createDebugger()
{
    if(!debugger)
    {
        debugger = new QLuaScriptDebugger();

        debugger->setWidgetFactory(new QLuaScriptDebuggerDefaultWidgetFactory(debugger));

        QObject::connect(debugger, SIGNAL(started()),
                         q, SIGNAL(evaluationResumed()));

        QObject::connect(debugger, SIGNAL(stopped()),
                         q, SIGNAL(evaluationSuspended()));

        if(autoShow) {
            QObject::connect(q, SIGNAL(evaluationSuspended()),
                             q, SLOT(_q_showStandardWindow()));
        }
    }
}

QLuaScriptEngineDebugger::QLuaScriptEngineDebugger(QObject *parent) :
    QObject(parent),
    d(new QLuaScriptEngineDebuggerPrivate())
{
    d->q = this;
}

QLuaScriptEngineDebugger::~QLuaScriptEngineDebugger()
{
}

void QLuaScriptEngineDebugger::attachTo(QLuaScriptEngine * engine)
{
    if(!engine) {
        detach();
        return;
    }

    d->createDebugger();
    d->debugAgent = new QLuaDebuggerAgent(d->debugger, engine);
    engine->setAgent(d->debugAgent);
    d->debugger->attachAgent(d->debugAgent);
}

void QLuaScriptEngineDebugger::detach()
{
    if(!d->debugger)
        return;    
    d->debugger->detachAgent();
    delete d->debugAgent;
}

bool QLuaScriptEngineDebugger::autoShowStandardWindow() const
{
    return d->autoShow;
}

void QLuaScriptEngineDebugger::setAutoShowStandardWindow(bool autoShow)
{
    if(autoShow == d->autoShow)
        return;
    if(autoShow) {
        QObject::connect(this,
                         SIGNAL(evaluationSuspended()),
                         this,
                         SLOT(_q_showStandardWindow()));
    } else {
        QObject::disconnect(this,
                         SIGNAL(evaluationSuspended()),
                         this,
                         SLOT(_q_showStandardWindow()));
    }
    d->autoShow = autoShow;
}

QMainWindow * QLuaScriptEngineDebugger::standardWindow() const
{
    if(d->standardWindow)
        return d->standardWindow;
    if(!QApplication::instance())
        return NULL;
    QLuaScriptEngineDebugger * self = const_cast<QLuaScriptEngineDebugger *>(this);
    QMainWindow * window = new QMainWindow();

    QDockWidget * scriptsDock = new QDockWidget(window);
    scriptsDock->setObjectName(QLatin1String("qtluadebugger_scriptsDockWidget"));
    scriptsDock->setWindowTitle(tr("Loaded Scripts"));
    scriptsDock->setWidget(debuggerWidget(ScriptsWidget));
    window->addDockWidget(Qt::LeftDockWidgetArea, scriptsDock);

    QDockWidget * breakpointsDock = new QDockWidget(window);
    breakpointsDock->setObjectName(QLatin1String("qtluadebugger_breakpointsDockWidget"));
    breakpointsDock->setWindowTitle(tr("Breakpoints"));
    breakpointsDock->setWidget(debuggerWidget(BreakpointsWidget));
    window->addDockWidget(Qt::LeftDockWidgetArea, breakpointsDock);

    QDockWidget * stackDock = new QDockWidget(window);
    stackDock->setObjectName(QLatin1String("qtluadebugger_stackDockWidget"));
    stackDock->setWindowTitle(tr("Stack"));
    stackDock->setWidget(debuggerWidget(StackWidget));
    window->addDockWidget(Qt::RightDockWidgetArea, stackDock);

    QDockWidget * localsDock = new QDockWidget(window);
    localsDock->setObjectName(QLatin1String("qtluadebugger_localsDockWidget"));
    localsDock->setWindowTitle(tr("Locals"));
    localsDock->setWidget(debuggerWidget(LocalsWidget));
    window->addDockWidget(Qt::RightDockWidgetArea, localsDock);

    QDockWidget * consoleDock = new QDockWidget(window);
    consoleDock->setObjectName(QLatin1String("qtluadebugger_consoleDockWidget"));
    consoleDock->setWindowTitle(tr("Console"));
    consoleDock->setWidget(debuggerWidget(ConsoleWidget));
    window->addDockWidget(Qt::BottomDockWidgetArea, consoleDock);

    QDockWidget * debugOutputDock = new QDockWidget(window);
    debugOutputDock->setObjectName(QLatin1String("qtluadebugger_debugOutputDockWidget"));
    debugOutputDock->setWindowTitle(tr("Debug Output"));
    debugOutputDock->setWidget(debuggerWidget(DebugOutputWidget));
    window->addDockWidget(Qt::BottomDockWidgetArea, debugOutputDock);

    QDockWidget * errorLogDock = new QDockWidget(window);
    errorLogDock->setObjectName(QLatin1String("qtluadebugger_errorLogDockWidget"));
    errorLogDock->setWindowTitle(tr("Debug Output"));
    errorLogDock->setWidget(debuggerWidget(ErrorLogWidget));
    window->addDockWidget(Qt::BottomDockWidgetArea, errorLogDock);

    window->tabifyDockWidget(errorLogDock, debugOutputDock);
    window->tabifyDockWidget(debugOutputDock, consoleDock);

    window->addToolBar(Qt::TopToolBarArea, self->createStandardToolBar());

    window->menuBar()->addMenu(self->createStandardMenu(window));

    QMenu * editMenu = window->menuBar()->addMenu(tr("Search"));
    editMenu->addAction(action(FindInScriptAction));
    editMenu->addAction(action(FindNextInScriptAction));
    editMenu->addAction(action(FindPreviousInScriptAction));
    editMenu->addSeparator();
    editMenu->addAction(action(GoToLineAction));

    QMenu * viewMenu = window->menuBar()->addMenu(tr("View"));

    QWidget * central = new QWidget();
    window->setCentralWidget(central);

    QVBoxLayout * vbox = new QVBoxLayout(central);
    vbox->setMargin(0);

    vbox->addWidget(debuggerWidget(CodeWidget));

    window->setWindowTitle(tr("QtLua Script Debugger"));
    window->setUnifiedTitleAndToolBarOnMac(true);

    QSettings settings;
    QVariant geometry = settings.value("QtLua/debugger/mainWindowGeometry");
    if(geometry.isValid())
        window->restoreGeometry(geometry.toByteArray());
    QVariant state = settings.value("QtLua/debugger/mainWindowState");
    if(state.isValid())
        window->restoreState(state.toByteArray());

    DebuggerWindowCloseNotifier * notifier = new DebuggerWindowCloseNotifier(window, self);
    connect(notifier, SIGNAL(widgetClosed()),
            action(ContinueAction),
            SLOT(trigger()));

    d->standardWindow = window;
    return window;
}

QWidget * QLuaScriptEngineDebugger::debuggerWidget(DebuggerWidget widget) const
{
    d->createDebugger();
    return d->debugger->debuggerWidget(widget);
}

QLuaScriptEngineDebugger::DebuggerState QLuaScriptEngineDebugger::state() const
{
    return RunningState;
}

QAction * QLuaScriptEngineDebugger::action(DebuggerAction action) const
{
    d->createDebugger();
    return d->debugger->action(action, d->q);
}

QMenu * QLuaScriptEngineDebugger::createStandardMenu(QWidget * parent) const
{
    d->createDebugger();
    return d->debugger->createStandardMenu(parent, d->q);
}

QToolBar * QLuaScriptEngineDebugger::createStandardToolBar(QWidget * parent) const
{
    d->createDebugger();
    return d->debugger->createStandardToolBar(parent, d->q);
}

#include "moc_qluascriptenginedebugger.cpp"
#include "qluascriptenginedebugger.moc"
